#pragma once

#include <queue>
#include <unistd.h>
#include "lock.hpp"

template<class T>
class BlockQueue  // 阻塞队列
{
public:
    BlockQueue(int capacity = 5)
        :_capacity(capacity)
    {
        // 初始化锁和条件变量
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_empty, nullptr);
        pthread_cond_init(&_full, nullptr);
    }

    bool isEmpty()
    {
        return _t.size() == 0;
    }

    bool isFull()
    {
        return _t.size() == _capacity;
    }

    void push(const T& t)  // 插入数据,生产
    {
        RAII_Mutex mutex(&_mtx); // 构造时上锁

        while(isFull()) // 阻塞队列为满
            pthread_cond_wait(&_full, &_mtx); // 满了我就等待
        
        _t.push(t); // 我插入数据,队列不是空了
        pthread_cond_signal(&_empty); // 唤醒你,来拿取数据
    }

    void pop(T& t)
    {
        RAII_Mutex mutex(&_mtx);  // 构造时上锁

        while (isEmpty()) // 队列中是空的, 我拿不了数据
            pthread_cond_wait(&_empty, &_mtx); // 那就等待
        
        t = _t.front(); // 拿出数据
        _t.pop(); // 在队列中删除数据

        pthread_cond_signal(&_full); // 拿了数据就唤醒你来添加数据
    }

    ~BlockQueue()
    {
        // 销毁锁和条件变量
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_full);
        pthread_cond_destroy(&_empty);
    }
private:
    std::queue<T> _t;   // 阻塞队列
    int _capacity;   // 阻塞队列的容量
    pthread_mutex_t _mtx; // 锁 -> 保证数据的安全
    pthread_cond_t _empty; // 条件变量,判断阻塞队列为空 -> 唤醒生产者来插入数据
    pthread_cond_t _full;  // 条件变量,判断阻塞队列为满 -> 唤醒消费者来消费数据
};

